{
 "cells": [
  {
   "cell_type": "markdown",
   "id": "f23526c2",
   "metadata": {},
   "source": [
    "#### Notebook style configuration (optional)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "5eb7194a",
   "metadata": {},
   "outputs": [],
   "source": [
    "from IPython.core.display import display, HTML\n",
    "style = open(\"./style.css\").read()\n",
    "display(HTML(\"<style>%s</style>\" % style))"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "1a8e0fb0",
   "metadata": {},
   "source": [
    "# Scales & projections\n",
    "\n",
    "Until now (lesson 1 & 2), we've been using exclusively linear scales along the x and y axis and Catesian projection. But matplotlib offers the possibility to use different scales (log scale, symlog scale, logit scale) and projections (polar, 3D, geographic). We'll review a few of them in this lesson.\n",
    "\n",
    "<img src=\"../images/projections.png\" width=\"50%\" align=\"left\" /> <img src=\"../images/scales.png\" width=\"50%\" /> These images come from the [cheatsheets](https://github.com/matplotlib/cheatsheets).\n"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "fd70e9e1",
   "metadata": {},
   "source": [
    "## 1. Scales\n",
    "\n",
    "Until now (lesson 1 & 2), we've been using exclusively linear scales along the x and y axis. But matplotlib offers the possibility to use different scales such as [log scale](https://matplotlib.org/stable/api/scale_api.html#matplotlib.scale.LogScale), [symlog scale](https://matplotlib.org/stable/api/scale_api.html#matplotlib.scale.SymmetricalLogScale), [logit scale](https://matplotlib.org/stable/api/scale_api.html#matplotlib.scale.LogitScale) or [user scale](https://matplotlib.org/stable/api/scale_api.html#matplotlib.scale.FuncScale) if none of the others fits your need."
   ]
  },
  {
   "cell_type": "markdown",
   "id": "41fc059b",
   "metadata": {},
   "source": [
    "Let's start with the log scale and observe what the cosine function looks like when the x scale is logarithmic."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "65969fef",
   "metadata": {},
   "outputs": [],
   "source": [
    "import numpy as np\n",
    "import matplotlib.pyplot as plt\n",
    "\n",
    "X = np.linspace(0, 10*np.pi, 5000)\n",
    "Y = np.cos(X)\n",
    "\n",
    "fig = plt.figure(figsize=(10,3), dpi=300)\n",
    "ax = plt.subplot(xscale=\"log\")\n",
    "\n",
    "plt.plot(X,Y)\n",
    "\n",
    "plt.show();"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "f2c7a77d",
   "metadata": {},
   "source": [
    "We can of course do the same with the y scale but we have to take care of not having negative or null values in Y."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "fa5aaf05",
   "metadata": {},
   "outputs": [],
   "source": [
    "import numpy as np\n",
    "import matplotlib.pyplot as plt\n",
    "\n",
    "X = np.linspace(0, 10*np.pi, 5000)\n",
    "Y = 1.001+np.cos(X)\n",
    "\n",
    "fig = plt.figure(figsize=(10,3), dpi=300)\n",
    "ax = plt.subplot(yscale=\"log\")\n",
    "\n",
    "plt.plot(X,Y)\n",
    "\n",
    "plt.show();"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "f2620a83",
   "metadata": {},
   "source": [
    "And we can also combine the two scales."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "fbbb754b",
   "metadata": {},
   "outputs": [],
   "source": [
    "import numpy as np\n",
    "import matplotlib.pyplot as plt\n",
    "\n",
    "X = np.linspace(0, 10*np.pi, 5000)\n",
    "Y = 1.001+np.cos(X)\n",
    "\n",
    "fig = plt.figure(figsize=(10,3), dpi=300)\n",
    "ax = plt.subplot(xscale = \"log\", yscale=\"log\")\n",
    "plt.plot(X,Y)\n",
    "\n",
    "plt.show();"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "6e266a1c",
   "metadata": {},
   "source": [
    "One question is what will happen if we change the line style? Does is adapt to the scale or not? Let's check."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "d03a709d",
   "metadata": {
    "scrolled": true
   },
   "outputs": [],
   "source": [
    "import numpy as np\n",
    "import matplotlib.pyplot as plt\n",
    "\n",
    "X = np.linspace(0, 10*np.pi, 5000)\n",
    "Y = 1.001+np.cos(X)\n",
    "\n",
    "fig = plt.figure(figsize=(10,3), dpi=300)\n",
    "ax = plt.subplot(xscale=\"log\", yscale=\"log\")\n",
    "\n",
    "plt.plot(X, Y, \"--\")\n",
    "\n",
    "plt.show();"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "238ccfcf",
   "metadata": {},
   "source": [
    "No changes. Dashed remains linear. What about markers then ?"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "bd485214",
   "metadata": {},
   "outputs": [],
   "source": [
    "import numpy as np\n",
    "import matplotlib.pyplot as plt\n",
    "\n",
    "X = np.linspace(0, 10*np.pi, 5000)\n",
    "Y = 1.001+np.cos(X)\n",
    "\n",
    "fig = plt.figure(figsize=(10,3), dpi=300)\n",
    "ax = plt.subplot(xscale=\"log\", yscale=\"log\")\n",
    "plt.plot(X, Y, \"-o\", markevery=(0,.05), markersize=3)\n",
    "\n",
    "plt.show();"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "e2923a27",
   "metadata": {},
   "source": [
    "Markers spacing follows approximately the curvilinear coordinates of the plot such that the y appear less and less space along the x axis.\n"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "3a86a17f",
   "metadata": {},
   "source": [
    "When you have an x or y domain that includes null or negative values, then you can use a symlog scale instead of a log scale. There two scales are very similar but the symlog scale includes an area around zero that is linear in order to avoid problem with the log. For negative value, the scale takes care of taking the absolute value and put back the negative sign after.\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "cbe1134b",
   "metadata": {},
   "outputs": [],
   "source": [
    "import numpy as np\n",
    "import matplotlib.pyplot as plt\n",
    "\n",
    "X = np.linspace(-10*np.pi, 10*np.pi, 5000)\n",
    "Y = np.sin(X)\n",
    "\n",
    "fig = plt.figure(figsize=(10,3), dpi=300)\n",
    "ax = plt.subplot(xscale=\"symlog\", yscale=\"symlog\")\n",
    "\n",
    "plt.plot(X, Y)\n",
    "\n",
    "ax.spines['right'].set_visible(False)\n",
    "ax.spines['top'].set_visible(False)\n",
    "ax.spines['bottom'].set_position(('data',0))\n",
    "ax.spines['left'].set_position(('data',0))\n",
    "\n",
    "plt.show();"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "417deb24",
   "metadata": {},
   "source": [
    "What is nice with scales is that they will adapt to almost every plot commands. For example, if we render a uniform scatter plot using a log x xaxis, there will be (visually) more dots on the right."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "429656e5",
   "metadata": {},
   "outputs": [],
   "source": [
    "np.random.seed(1);\n",
    "X = np.random.uniform(0.001, 1.0, 5000)\n",
    "Y = np.random.uniform(0.001, 0.5, 5000)\n",
    "\n",
    "fig = plt.figure(figsize=(10,3), dpi=300)\n",
    "ax = plt.subplot(xscale=\"log\", yscale=\"linear\")\n",
    "ax.scatter(X, Y, 1, marker='o', cmap=\"Blues\")\n",
    "\n",
    "plt.tight_layout()\n",
    "plt.show();"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "3ad7cc53",
   "metadata": {},
   "source": [
    "Same is true with bar plot using a log scale that results an interesting rendering. Note that this case, the bars are thinner and thinner while it was not the case with dots in the previous example. The reason is that bars derive from a mesh object while it is not the case for markers."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "beac96df",
   "metadata": {
    "scrolled": true
   },
   "outputs": [],
   "source": [
    "np.random.seed(1)\n",
    "X = 1+np.arange(0,32)\n",
    "Y = np.linspace(0.5, 1.0, len(X))\n",
    "\n",
    "fig = plt.figure(figsize=(10,4), dpi=300)\n",
    "ax = plt.subplot(xscale=\"log\")\n",
    "ax.bar(X, Y)\n",
    "\n",
    "plt.show();"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "b0d1a826",
   "metadata": {},
   "source": [
    "## 2. Projections\n",
    "\n",
    "Projections is another important concept to know for matplotlib. Until now, we've plotting in the 2D Cartesian space even though we distorted it a bit using scales. But we can also project our data in a different spaces. Well, technically, the data will still live in the Cartesian space because your monitor is Cartesian, but we'll pretend it lives in a different space. Let's try with a polar space."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "ce2b434f",
   "metadata": {
    "scrolled": false
   },
   "outputs": [],
   "source": [
    "import numpy as np\n",
    "import matplotlib.pyplot as plt\n",
    "\n",
    "fig = plt.figure(figsize=(10,10), dpi=300)\n",
    "ax = plt.subplot(projection=\"polar\")\n",
    "\n",
    "plt.show();"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "ab90815f",
   "metadata": {},
   "source": [
    "The polar projection comes with specific settings because some settings do not make sense anymore, such as for example x and y limits. So let's play with the new settings."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "fcf1c8a1",
   "metadata": {},
   "outputs": [],
   "source": [
    "import numpy as np\n",
    "import matplotlib.pyplot as plt\n",
    "\n",
    "R = np.random.uniform(1, 5, 250)\n",
    "T = np.random.uniform(0, 45/180*np.pi, 250)\n",
    "\n",
    "fig = plt.figure(figsize=(10,4), dpi=300)\n",
    "\n",
    "ax = plt.subplot(1, 3, 1, projection=\"polar\")\n",
    "ax.scatter(T, R, 1, \"black\", zorder=10)\n",
    "ax.set_rmin(1)                # Minimum radius\n",
    "ax.set_rmax(5)                # Maximum radius\n",
    "ax.set_rorigin(1)             # Origin position\n",
    "ax.set_rticks(1+np.arange(5)) # Maximum radius\n",
    "ax.set_thetamin(0)            # Minimum angle (degrees)\n",
    "ax.set_thetamax(45)           # Maximum radius (degrees)\n",
    "ax.set_theta_offset(0)        # Origin orientation\n",
    "\n",
    "ax = plt.subplot(1, 3, 2, projection=\"polar\")\n",
    "ax.scatter(T, R, 1, \"black\", zorder=10)\n",
    "ax.set_rmin(1) \n",
    "ax.set_rmax(5) \n",
    "ax.set_rorigin(0)\n",
    "ax.set_rticks([1,5])\n",
    "ax.set_thetamin(0)\n",
    "ax.set_thetamax(180)\n",
    "ax.set_theta_offset(0)\n",
    "ax.set_xticks([])\n",
    "\n",
    "ax = plt.subplot(1, 3, 3, projection=\"polar\")\n",
    "ax.scatter(T, R, 1, \"black\", zorder=10)\n",
    "ax.set_rmin(1)\n",
    "ax.set_rmax(5) \n",
    "ax.set_rorigin(-10)\n",
    "ax.set_rticks([1,2,3,4,5])\n",
    "ax.set_thetamin(0)\n",
    "ax.set_thetamax(45)\n",
    "ax.set_theta_offset(0.5 * 3*np.pi/4)\n",
    "ax.set_xticks([0, np.pi/4])\n",
    "\n",
    "plt.show();"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "3096dea5",
   "metadata": {},
   "source": [
    "As you can see, a polar plot can be rendered in a number of different ways. You're not stuck with the regular full polar axes."
   ]
  },
  {
   "cell_type": "markdown",
   "id": "e4a192b6",
   "metadata": {},
   "source": [
    "Unfortunately, imshow does not work with polar projection and we have to use [pcolormesh](https://matplotlib.org/stable/api/_as_gen/matplotlib.pyplot.pcolormesh.html) instead."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "48c237e1",
   "metadata": {},
   "outputs": [],
   "source": [
    "import numpy as np\n",
    "import matplotlib.pyplot as plt\n",
    "\n",
    "\n",
    "X,Y = np.meshgrid(np.linspace(-3, 3, 200),\n",
    "                  np.linspace(-3, 3, 200))\n",
    "Z =(1-X/2+X**5+Y**3)*np.exp(-X**2-Y**2)\n",
    "\n",
    "fig = plt.figure(figsize=(10,10), dpi=300)\n",
    "\n",
    "# Regular imshow\n",
    "ax = plt.subplot(1, 3, 1)\n",
    "ax.imshow(Z)\n",
    "ax.set_xticks([])\n",
    "ax.set_yticks([])\n",
    "\n",
    "# Full polar \"imshow\" (pcolormesh)\n",
    "ax = plt.subplot(1, 3, 2, projection=\"polar\")\n",
    "ax.pcolormesh(np.linspace(0, 2*np.pi, 200),\n",
    "              np.linspace(0, 1, 200), Z[::-1,::-1], shading='auto')\n",
    "ax.set_rticks([])\n",
    "ax.set_xticks([])\n",
    "\n",
    "# Partial polar \"imshow\" (pcolormesh)\n",
    "ax = plt.subplot(1, 3, 3, projection=\"polar\")\n",
    "ax.pcolormesh(np.linspace(0, np.pi/2, 200),\n",
    "              np.linspace(0, 1, 200), Z[::-1,::-1], shading='auto')\n",
    "ax.set_rticks([])\n",
    "ax.set_xticks([])\n",
    "ax.set_rmin(0); ax.set_rmax(1)\n",
    "ax.set_rorigin(-.5)\n",
    "ax.set_thetamin(0)\n",
    "ax.set_thetamax(90)\n",
    "\n",
    "plt.show();"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "122b2cce",
   "metadata": {},
   "source": [
    "## 3. Exercises\n"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "b2a5f6fc",
   "metadata": {},
   "source": [
    "In the figure below, a circle is plotted using linear axis, log axis and a combination. The goal is to reproduce the figure, especially the sub-figure on the right. To do that, you'll need to transform manually your data.\n",
    "\n",
    "<img src=\"../images/03-exercise-1.png\" width=\"100%\" />"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.8.10"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 5
}
